﻿using System;
using System.Linq;
using System.Collections.Generic;
using System.Web.Mvc;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Moq;
using SportsStore.Domain.Abstract;
using SportsStore.Domain.Entities;
using SportsStore.WebUI.Models;
using SportsStore.WebUI.Controllers;
using SportsStore.WebUI.HtmlHelpers;

namespace SportsStore.UnitTests
{
    [TestClass]
    public class CatalogueTests
    {
        [TestMethod]
        public void Can_Paginate()
        {
            // Arrange
            var mock = new Mock<IProductRepository>();
            mock.Setup( repo => repo.Products )
                .Returns( new Product[]
                {
                    new Product { ProductID = 1, Name = "P1" },
                    new Product { ProductID = 2, Name = "P2" },
                    new Product { ProductID = 3, Name = "P3" },
                    new Product { ProductID = 4, Name = "P4" },
                    new Product { ProductID = 5, Name = "P5" }
                }.AsQueryable() );
            var controller = new ProductController( mock.Object );
            controller.PageSize = 3;

            // Act
            var result = (ProductListViewModel) controller.List( null, 2 ).Model;

            // Assert
            var productArray = result.Products.ToArray();
            Assert.AreEqual( 2, productArray.Length );
            Assert.AreEqual( "P4", productArray[ 0 ].Name );
            Assert.AreEqual( "P5", productArray[ 1 ].Name );
        }

        [TestMethod]
        public void Can_Generate_Page_Links()
        {
            // Arrange - define an HTML helper - we need to do this
            // in order to apply the extension method
            HtmlHelper myHelper = null;

            // Arrange - create PagingInfo data
            var pagingInfo = new PagingInfo
            {
                CurrentPage = 2,
                TotalItems = 28,
                ItemsPerPage = 10
            };

            // Arrange - set up the delegate using a lambda expression
            Func<int, string> pageUrlDelegate = index => String.Format( "Page {0}", index );

            // Act
            MvcHtmlString result = myHelper.PageLinks( pagingInfo, pageUrlDelegate );

            // Assert
            Assert.AreEqual( String.Format( "{0}{1}{2}",
                @"<a href=""Page 1"">1</a>",
                @"<a class=""selected"" href=""Page 2"">2</a>",
                @"<a href=""Page 3"">3</a>" ),
                result.ToString() );
        }

        [TestMethod]
        public void Can_Send_Pagination_View_Model()
        {
            // Arrange
            var mock = new Mock<IProductRepository>();
            mock.Setup( repo => repo.Products )
                .Returns( new Product[]
                {
                    new Product { ProductID = 1, Name = "P1" },
                    new Product { ProductID = 2, Name = "P2" },
                    new Product { ProductID = 3, Name = "P3" },
                    new Product { ProductID = 4, Name = "P4" },
                    new Product { ProductID = 5, Name = "P5" }
                }.AsQueryable() );

            // Arrange
            var controller = new ProductController( mock.Object );
            controller.PageSize = 3;

            // Act
            ProductListViewModel result = (ProductListViewModel) controller.List( null, 2 ).Model;

            // Assert
            var pagingInfo = result.PagingInfo;
            Assert.AreEqual( 2, pagingInfo.CurrentPage );
            Assert.AreEqual( 3, pagingInfo.ItemsPerPage );
            Assert.AreEqual( 5, pagingInfo.TotalItems );
            Assert.AreEqual( 2, pagingInfo.TotalPages );
        }

        [TestMethod]
        public void Can_Filter_Products()
        {
            // Arange
            // - create the mock repository
            var mock = new Mock<IProductRepository>();
            mock.Setup( repo => repo.Products )
                .Returns( new Product[]
                {
                    new Product { ProductID = 1, Name = "P1", Category = "Cat1" },
                    new Product { ProductID = 2, Name = "P2", Category = "Cat2" },
                    new Product { ProductID = 3, Name = "P3", Category = "Cat1" },
                    new Product { ProductID = 4, Name = "P4", Category = "Cat2" },
                    new Product { ProductID = 5, Name = "P5", Category = "Cat3" }
                }.AsQueryable() );

            // Arrange - create controller and make the page size 3 items
            var controller = new ProductController( mock.Object );
            controller.PageSize = 3;

            // Action
            var result = ( (ProductListViewModel) controller.List( "Cat2", 1 ).Model ).Products.ToArray();

            // Assert
            Assert.AreEqual( 2, result.Length );
            Assert.IsTrue( result[ 0 ].Name == "P2" && result[ 0 ].Category == "Cat2" );
            Assert.IsTrue( result[ 1 ].Name == "P4" && result[ 1 ].Category == "Cat2" );
        }

        [TestMethod]
        public void Generate_Category_Specific_Product_Count()
        {
            // Arrange
            // - create the mock repository
            var mock = new Mock<IProductRepository>();
            mock.Setup( repo => repo.Products )
                .Returns( new Product[]
                {
                    new Product { ProductID = 1, Name = "P1", Category = "Cat1" },
                    new Product { ProductID = 2, Name = "P2", Category = "Cat2" },
                    new Product { ProductID = 3, Name = "P3", Category = "Cat1" },
                    new Product { ProductID = 4, Name = "P4", Category = "Cat2" },
                    new Product { ProductID = 5, Name = "P5", Category = "Cat3" }
                }.AsQueryable() );

            // Arrange - create controller and make the page size 3 items
            var controller = new ProductController( mock.Object );
            controller.PageSize = 3;

            // Action - test different products for different categories
            var resultCat1 = ( (ProductListViewModel) controller.List( "Cat1" ).Model ).PagingInfo.TotalItems;
            var resultCat2 = ( (ProductListViewModel) controller.List( "Cat2" ).Model ).PagingInfo.TotalItems;
            var resultCat3 = ( (ProductListViewModel) controller.List( "Cat3" ).Model ).PagingInfo.TotalItems;
            var resultAll  = ( (ProductListViewModel) controller.List( null ).Model ).PagingInfo.TotalItems;

            // Assert
            Assert.AreEqual( 2, resultCat1 );
            Assert.AreEqual( 2, resultCat2 );
            Assert.AreEqual( 1, resultCat3 );
            Assert.AreEqual( 5, resultAll );
        }

        [TestMethod]
        public void Can_Create_Categories()
        {
            // Arrange - create the mock repository
            var mock = new Mock<IProductRepository>();
            mock.Setup( product => product.Products )
                .Returns( new Product[]
                {
                    new Product { ProductID = 1, Name = "P1", Category = "Apples" },
                    new Product { ProductID = 2, Name = "P2", Category = "Apples" },
                    new Product { ProductID = 3, Name = "P3", Category = "Plums" },
                    new Product { ProductID = 4, Name = "P4", Category = "Oranges" },
                }.AsQueryable() );

            // Arrange - create the controller
            var controller = new NavController( mock.Object );

            // Act - get the set of categories
            var results = ( (IEnumerable<string>) controller.Menu().Model ).ToArray();

            // Assert
            Assert.AreEqual( 3, results.Length );
            Assert.AreEqual( "Apples", results[ 0 ] );
            Assert.AreEqual( "Oranges", results[ 1 ] );
            Assert.AreEqual( "Plums", results[ 2 ] );
        }

        [TestMethod]
        public void Indicates_Selected_Category()
        {
            // Arrange - create the mock repository
            var mock = new Mock<IProductRepository>();
            mock.Setup( product => product.Products )
                .Returns( new Product[]
                {
                    new Product { ProductID = 1, Name = "P1", Category = "Apples" },
                    new Product { ProductID = 2, Name = "P2", Category = "Apples" },
                    new Product { ProductID = 3, Name = "P3", Category = "Plums" },
                    new Product { ProductID = 4, Name = "P4", Category = "Oranges" },
                }.AsQueryable() );

            // Arrange - create the controller
            var controller = new NavController( mock.Object );

            // Arrange - define the category selected
            var categoryToSelect = "Apples";

            // Action
            var result = controller.Menu( categoryToSelect ).ViewBag.SelectedCategory;

            // Assert
            Assert.AreEqual( categoryToSelect, result );
        }
    }
}
